
	/***********************************************************************************************

		@API
		Class: jsl.EventMap

		Events:
			map				EventMap					()
			events    events         		(ids*..)
			data			throwEvent				(id, e*)
			b					hasListeners			(event)

			map				enableEvent				(id)
			map				disableEvent			(id)
			map				enableEvents			(ids*..)
			map				disableEvents			(ids*..)

			map				removeEvent				(id)
			map				removeEvents			(ids..)

		Behaviors:
			map				behavior					(behavior*)
			map				eventAction				(id, action*)

		Listeners:
			map				bind							(event, action/data)
			map				unbind						(id)

			map				enableListener		(id)
			map				disableListener		(id)
			map				enableListeners		(ids..)
			map				disableListeners	(ids..)


		Note:
			3 players:	events, behaviors, listeners
			data:				o{ action, priority, enabled, id }
			priority:		s(top,high,normal,low,bottom)
			throwEvent:	the event object e is shared through all listeners and allow comunication

	************************************************************************************************/

	//-------------------------------------------------------------------------------
	jsl.Class("jsl.EventMap",
		{

			//-------------------------------------------------------------------------------
			EventMap: function() {//!
				if(arguments.length) throw jsl.ParamError()//!
				this.__evs = []
			},
      //-------------------------------------------------------------------------------
      events: function() {
      	var map, ids, v, e, a, l, id

      	//1-settings
      	if(arguments.length) {

	        for(a = 0, l = arguments.length; a < l; a++) {

	        	id = arguments[a]//!
						jsl.validate(id, "s+", "events[" + a + "]")
						if(this["__e_" + id])	throw jsl.ParamError("AlreadyDefined", [ "events(" + id + ")" ])//!

						e					= []
						e.id			= id + ""
						e.enabled = true
						e.obj			= this

						this["__e_" + id]			= e
						this.__evs["e_" + id]	= e
						this.__evs.push(e)
	        }

					v = this
      	}

      	//2-reading
      	else {
	      	map = this
	      	ids = {}
	        v 	= []

	      	while(map && map.__evs) {
	      		for(a = 0, l = map.__evs.length; a < l; a++)  {

	      			id = map.__evs[a].id
	      			if(!ids[id]) {
		        		v.push(id)
		        		ids[id] = true
		        	}
	      		}

		        map = map.__proto
	      	}

	      	v.sort()
      	}

        return v
      },
			//-----------------------------------------------------------------------------------------------
			throwEvent: function(id, e) {
				var evt, lid, a, l, dt, obj, ls

				//!
				if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")

				if(!this["__e_" + id]) 			throw jsl.ParamError("NotDefined:id")
				if(arguments.length === 2) 	jsl.validate(e, "o", "e")//!

				//1-sequence stopped
				if(e && e instanceof jsl.Event && !e.__sequence)
					obj = {
						sequence: 		e.__sequence,
						listeners:		e.__listeners,
						action: 	 		e.__action,
						propagation:	e.__propagation
					}

				//2-sequence not stopped
				else {

					dt = this.__evs["e_" + id]

					//checks if the event on the current object is enabled
					if(!dt || dt.enabled) {

						if(!e)	evt = new jsl.Event
						else		evt = e instanceof jsl.Event ? e : new jsl.Event(e)

						evt.eventId			= id + ""
						evt.__listeners = true
						evt.__action		= true

						//the listener must be on the current object
						lid	= "on" + id.charAt(0).toUpperCase() + id.slice(1)
						if(this[lid] != null && this.hasOwnProperty(lid)) {//!
							if(!jsl.isFunction(this[lid]))	throw jsl.TypeError("BadType", [ lid , "function" ])//!

							try 			{ this[lid](evt) }
							catch(e)	{	throw jsl.Error(e).trace(this.__name + "." + lid, [ evt ])	}
						}

						if(evt.__listeners && dt && dt.length) {

							//a listener can remove another listener and therefore change the dt content
							dt = dt.slice()
							for(a = 0, l = dt.length; a < l; a++) {

								ls = dt[a]

								if(ls.enabled && !ls.removed) {
									try 			{ ls.action.call(this, evt) }
									catch(e) 	{ throw jsl.Error(e).trace(this.__name + "." + (ls.id || lid), [ evt ])	}

									if(!evt.__listeners)
										break
								}
							}
						}

						if(evt.__action && this["__e_" + id + "_a"])
							this["__e_" + id + "_a"](evt)

						obj = {
							sequence: 		evt.__sequence,
							listeners:		evt.__listeners,
							action: 	 		evt.__action,
							propagation:	evt.__propagation
						}
					}
					else
						obj = {
							sequence: 		true,
							listeners:		true,
							action: 	 		true,
							propagation: 	true
						}
				}

				return obj
			},
			//-------------------------------------------------------------------------------
			hasListeners: function(event) {//!
				if(arguments.length !== 1) throw jsl.ParamError()
				jsl.validate(event, "s+", "event")
				if(!this["__e_" + event])	throw jsl.ParamError("NotDefined:event")//!

				return 	!!(this.hasOwnProperty("on" + event.charAt(0).toUpperCase() + event.slice(1)) ||
								this.__evs["e_" + event] && this.__evs["e_" + event].length)
			},
			//-------------------------------------------------------------------------------
			enableEvent: function(id) {
				var e//!
				if(arguments.length !== 1)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")
				if(!this["__e_" + id]) throw jsl.ParamError("NotDefined:id")//!

				e = this.__evs["e_" + id]
				if(e) e.enabled = true
				return this
			},
			//-------------------------------------------------------------------------------
			disableEvent: function(id) {
				var e//!
				if(arguments.length !== 1)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")
				if(!this["__e_" + id])			throw jsl.ParamError("NotDefined:id")//!

				e = this.__evs["e_" + id]

				if(!e) {
					e 				= []
					e.id			= id + ""
					e.enabled = false

					this.__evs["e_" + id] = e
					this.__evs.push(e)
				}

				e.enabled = false
				return this
			},
			//-------------------------------------------------------------------------------
			enableEvents: function() {
				var es, a, l

				es = arguments.length ? arguments : this.events()
				for(a = 0, l = es.length; a < l; a++) this.enableEvent(es[a])
				return this
			},
			//-------------------------------------------------------------------------------
			disableEvents: function() {
				var es, a, l

				es = arguments.length ? arguments : this.events()
				for(a = 0, l = es.length; a < l; a++) this.disableEvent(es[a])
				return this
			},
			//-------------------------------------------------------------------------------
			removeEvent: function(id) {
				var evs, ev//!
				if(arguments.length !== 1)		throw jsl.ParamError()
				jsl.validate(id, "s+", "id")
				if(!this["__e_" + id])							throw jsl.ParamError("NotDefined:id")
				if(this["__e_" + id].obj !== this)	throw jsl.ParamError("OperationDenied")//!

				evs = this.__evs
				delete evs["e_" + id]
				delete this["__e_" + id]
				delete this["__e_" + id + "_a"]

				for(var a = 0, l = evs.length; a < l; a++)
					if(evs[a].id == id) {

						ev = evs[a]
						evs.splice(a, 1)

						for(var b = 0; b < ev.length; b++) {
							delete evs["l_" + ev[b].id]
							delete this["__l_" + ev[b].id]
						}

						break
					}

				return this
			},
			//-------------------------------------------------------------------------------
			removeEvents: function() {
				var args, a , l//!
				if(!arguments.length)	throw jsl.ParamError()//!

				args = arguments
				for(a = 0, l = args.length; a < l; a++) {//!
					jsl.validate(args[a], "s+", "ids[" + a + "]")
					if(!this["__e_" + args[a]])	throw jsl.ParamError("NotDefined", [ "ids[" + a + "]" ])//!
					this.removeEvent(args[a])
				}
				return this
			},
			//-------------------------------------------------------------------------------
			behavior: function(behavior) {
				var es, f, a, l, v//!
				if(arguments.length > 1)	throw jsl.ParamError()
				if(arguments.length)			jsl.validate(behavior, "o", "behavior")//!

				es	= this.events()
				v		= this

				if(behavior)
					for(a = 0, l = es.length; a < l; a++)	{

						f = behavior[es[a]]

						if(f !== undefined) {//!
							if(f && typeof f !== "function")	throw jsl.ParamError("BadType", [ "behavior." + es[a], "function" ])//!
							this["__e_" + es[a] + "_a"] = f
						}
					}
				else {
					v = {}
					for(a = 0, l = es.length; a < l; a++)
						v[es[a]] = this["__e_" + es[a] + "_a"] || null
				}

				return v
			},
			//-------------------------------------------------------------------------------
			eventAction: function(id, action) {
				var v//!
				if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")
				if(!this["__e_" + id])												throw jsl.ParamError("NotDefined:id")
				if(arguments.length === 2 && action != null)	jsl.validate(action, "f", "action")//!

				if(arguments.length === 2) {
					if(action)  this["__e_" + id + "_a"] = action
					else delete this["__e_" + id + "_a"]
					v = this
				}
				else
					v = this["__e_" + id + "_a"] || null

				return v
			},
			//-------------------------------------------------------------------------------
			bind: function(event, data) {
				var e, l, evs, p//!
				if(arguments.length !== 2)	throw jsl.ParamError()
				jsl.validate(event, "s+", "event")
				if(!this["__e_" + event])	throw jsl.ParamError("NotDefined:event")
				jsl.validate(data, "o", "data")//!

				evs	= this.__evs
				e 	= evs["e_" + event]

				if(!e) {
					e 				= []
					e.id			= event + ""
					e.enabled = true

					evs["e_" + event] = e
					evs.push(e)
				}

				if(jsl.isFunction(data))
					l = {
						event: 		event + "",
						id:				null,
						action:		data,
						priority:	0,
						enabled:	true,
						eobj:			e
					}

				else {//!
					if(data.id != null) {
						jsl.validate(data.id, "s+", "data.id")
						if(evs["l_" + data.id])	throw jsl.ParamError("AlreadyDefined:data.id")
					}

          if(data.priority 	!= null)	jsl.validate(data.priority, "si(top,high,normal,low,bottom)", "data.priority")
	        if(data.enabled 	!= null)	jsl.validate(data.enabled	, "b", "data.enabled")
					jsl.validate(data.action, "f", "data.action")//!

					p = 0
					if(data.priority) {

						p = data.priority.toLowerCase()
						switch(p) {
							case "top": 		p = +2; break
							case "high": 		p = +1; break
							case "normal": 	p = +0; break
							case "low": 		p = -1; break
							case "bottom": 	p = -2; break
						}
					}

					l	= {
            event:    event + "",
						action: 	data.action,
						id:				data.id 			!= null ? data.id + "" 		: null,
						enabled:	data.enabled 	!= null ? !!+data.enabled : true,
						priority: p,
						eobj:			e
					}
        }

				e.sort(function(a, b) { return b.priority - a.priority })

				if(l.id) {
					this["__l_" + l.id]	= l
					evs["l_" + l.id]		= l
				}

				e.push(l)
				return this
			},
			//-------------------------------------------------------------------------------
			unbind: function(id) {
				var a, l, evs, eobj//!
				if(arguments.length !== 1)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")//!

				evs = this.__evs
				l 	= evs["l_" + id]//!
				if(!l)	throw jsl.ParamError("NotDefined:id")//!

				delete evs["l_" + id]

				eobj = l.eobj
				for(a = 0, l = eobj.length; a < l; a++)
					if(eobj[a].id == id) {
						eobj[a].removed = true
						eobj.splice(a, 1)
						break
					}

				return this
		  },
			//-------------------------------------------------------------------------------
			enableListener: function(id) {
				var ls//!
				if(arguments.length !== 1)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")//!

				ls = this.__evs["l_" + id]//!
				if(!ls)	throw jsl.ParamError("NotDefined:id")//!
				ls.enabled = true
				return this
		  },
			//-------------------------------------------------------------------------------
			disableListener: function(id) {
				var ls//!
				if(arguments.length !== 1)	throw jsl.ParamError()
				jsl.validate(id, "s+", "id")//!

				ls = this.__evs["l_" + id]//!
				if(!ls)	throw jsl.ParamError("NotDefined:id")//!
				ls.enabled = false
				return this
		  },
			//-------------------------------------------------------------------------------
			enableListeners: function() {//!
				if(!arguments.length)	throw jsl.ParamError()//!

				for(var a = 0, l = arguments.length; a < l; a++)
					this.enableListener(arguments[a])

				return this
		  },
			//-------------------------------------------------------------------------------
			disableListeners: function() {//!
				if(!arguments.length)	throw jsl.ParamError()//!

				for(var a = 0, l = arguments.length; a < l; a++)
					this.disableListener(arguments[a])

				return this
		  }
		})
